#include <stdio.h>
#include <stdlib.h> //malloc/free
#include <curl/curl.h> //libcurl
#include <string.h> //strlen
#include "auth.h" //get_token
#include "cJSON.h"

//读取音频数据到内存，返回数据大小
size_t stt_load_file(const char* file, char** pbuf)
{
    FILE* fp = fopen(file, "r");
    if (fp == NULL)
    {
        perror("fopen failed");
        return 0;
    }

    fseek(fp, 0, SEEK_END);
    long size = ftell(fp);

    *pbuf = malloc(size);
    fseek(fp, 0, SEEK_SET);

    fread(*pbuf, 1, size, fp);
    fclose(fp);

    return size;
}

char* stt_send_request(const char* token, const char* audio, size_t size)
{
    FILE* fp;
    //响应消息的地址
    char* response = NULL;
    //响应消息的长度
    size_t resplen = 0;
    //创建内存文件，当通过文件句柄写入数据时，会自动分配内存
    fp = open_memstream(&response, &resplen);
    if (fp == NULL) //打开文件失败，打印错误信息并退出
    {
        perror("open_memstream() failed");
        return NULL;
    }

    //初始化HTTP客户端
    CURL* curl = curl_easy_init();
    if (curl == NULL)
    {
        perror("curl_easy_init() failed");
        return NULL;
    }

    //拼接API地址和参数
    char* url = NULL;
    asprintf(&url, "http://vop.baidu.com/server_api?cuid=liuyu&token=%s&dev_pid=1537", token);

    //准备HTTP请求消息，设置API地址（URI）
    curl_easy_setopt(curl, CURLOPT_URL, url);
    //创建请求头链表
    struct curl_slist* headers = NULL;
    //向链表中增加头部字段
    headers = curl_slist_append(headers, "Content-Type: audio/pcm;rate=16000");
    //设置HTTP请求头部字段
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    //配置客户端，使用HTTP的POST方法发送请求消息
    //curl_easy_setopt(curl, CURLOPT_POST, 1);
    //配置需要通过POST请求消息发送给服务器的数据
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, audio);
    //指定发送数据的长度
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, size);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
    //打印HTTP请求和响应消息头
    //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);

    //发送HTTP请求消息，等待服务器的响应消息
    CURLcode error = curl_easy_perform(curl);
    if (error != CURLE_OK)
    {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(error));
        curl_easy_cleanup(curl);
        fclose(fp);
        free(response);
        free(url);
        return NULL;
    }

    //释放HTTP客户端申请的资源
    curl_easy_cleanup(curl);

    //关闭内存文件
    fclose(fp);

    //释放asprintf申请的内存
    free(url);

    return response;
}

/*
解析服务器响应报文
{
    "corpus_no": "6433214037620997779",
    "err_msg": "success.",
    "err_no": 0,
    "result": [
        "北京科技馆，"
    ],
    "sn": "371191073711497849365"
}
*/
char* stt_process_response( char* response)
{
    cJSON* json = cJSON_Parse(response);
    if (json == NULL)
    {
        const char* error_pos = cJSON_GetErrorPtr();
        if (error_pos != NULL)
        {
            fprintf(stderr, "Error before: %s\n", error_pos);
        }
        return NULL;
    }

    cJSON* err_no = cJSON_GetObjectItemCaseSensitive(json, "err_no");
    if(err_no->valueint != 0)
    {
        cJSON* err_msg = cJSON_GetObjectItemCaseSensitive(json,"err_msg");
        fprintf(stderr,"%s\n",err_msg->valuestring);
        return NULL;
    }

    cJSON* result = cJSON_GetObjectItemCaseSensitive(json,"result");
    //获取数组中的第一个元素
    cJSON* text = cJSON_GetArrayItem(result,0);
    
    return text->valuestring;
}

char* robot_make_request(const char* apikey,  char* text)
{
    //判断输入的字符串长度不为0
    if (strlen(text) == 0)
    {
        return NULL;
    }

    cJSON* request = cJSON_CreateObject();

    cJSON* perception = cJSON_CreateObject();
    cJSON* inputText = cJSON_CreateObject();

    cJSON_AddStringToObject(inputText, "text", text);
    cJSON_AddItemToObject(perception, "inputText", inputText);
    cJSON_AddItemToObject(request, "perception", perception);

    cJSON* userInfo = cJSON_CreateObject();
    cJSON_AddStringToObject(userInfo, "apiKey", apikey);
    cJSON_AddStringToObject(userInfo, "userId", "gao");
    cJSON_AddItemToObject(request, "userInfo", userInfo);

    //将JSON数据结构转为字符串
    return cJSON_Print(request);
}

//作业：完成此函数，发送请求报文给图灵机器人服务器，等待服务器的响应报文
//     收到响应报文后，不需要解析，直接通过函数返回值返回JSON字符串
char* robot_send_request(const char* request)
{
    FILE* fp;
    //响应消息的地址
    char* response = NULL;
    //响应消息的长度
    size_t resplen = 0;
    //创建内存文件，当通过文件句柄写入数据时，会自动分配内存
    fp = open_memstream(&response, &resplen);
    if (fp == NULL) //打开文件失败，打印错误信息并退出
    {
        perror("open_memstream() failed");
        return NULL;
    }

    //初始化HTTP客户端
    CURL* curl = curl_easy_init();
    if (curl == NULL)
    {
        perror("curl_easy_init() failed");
        return NULL;
    }

    //准备HTTP请求消息，设置API地址（URI）
    curl_easy_setopt(curl, CURLOPT_URL, "http://openapi.tuling123.com/openapi/api/v2");
    //配置客户端，使用HTTP的POST方法发送请求消息
    curl_easy_setopt(curl, CURLOPT_POST, 1);
    //配置需要通过POST请求消息发送给服务器的数据
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
    //打印HTTP请求和响应消息头
    //curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);

    //发送HTTP请求消息，等待服务器的响应消息
    CURLcode error = curl_easy_perform(curl);
    if (error != CURLE_OK)
    {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(error));
        curl_easy_cleanup(curl);
        fclose(fp);
        free(response);
        return NULL;
    }

    //释放HTTP客户端申请的资源
    curl_easy_cleanup(curl);

    //关闭内存文件
    fclose(fp);

    return response;
}

/*
处理机器人响应报文
{
    "intent": {
        "actionName": "",
        "code": 10004,
        "intentName": ""
    },
    "results": [
        {
            "groupType": 1,
            "resultType": "text",
            "values": {
                "text": "真有礼貌，人家喜欢你呢"
            }
        }
    ]
}
 */
char* robot_process_response(char* response)
{
    cJSON* json = cJSON_Parse(response);
    if (json == NULL)
    {
        const char* error_pos = cJSON_GetErrorPtr();
        if (error_pos != NULL)
        {
            fprintf(stderr, "Error before: %s\n", error_pos);
            
        }
    }

    cJSON* intent = cJSON_GetObjectItemCaseSensitive(json, "intent");
    if (intent == NULL)
    {
        fprintf(stderr, "Get intent object failed\n");
        cJSON_Delete(json);
        
    }
    cJSON* code = cJSON_GetObjectItemCaseSensitive(intent, "code");
    if (code->valueint < 10000) //code字段的值如果小于10000，认为机器人出错
    {
        fprintf(stderr, "Robot error: %d\n", code->valueint);
        cJSON_Delete(json);
       
    }

    cJSON* results = cJSON_GetObjectItemCaseSensitive(json, "results");
    cJSON* result;
    cJSON_ArrayForEach(result, results) //遍历数组
    {
        cJSON* resultType = cJSON_GetObjectItemCaseSensitive(result, "resultType");
        if (strcmp(resultType->valuestring, "text") == 0) //只打印文本消息
        {
            cJSON* values = cJSON_GetObjectItemCaseSensitive(result, "values");
            cJSON* text = cJSON_GetObjectItemCaseSensitive(values, "text");
            puts(text->valuestring);
            return text->valuestring;
        }
    }
    
    cJSON_Delete(json);
}

void text2speech(const char* token,  char* text)
{
    CURL* curl = curl_easy_init();

    //发送到百度云的字符串需要进行2次URL编码
    char* temp = curl_easy_escape(curl, text, strlen(text));
    char* data = curl_easy_escape(curl, temp, strlen(temp));
    curl_free(temp);

    //拼接POST请求发送的数据
    char* postdata;
    asprintf(&postdata, "tex=%s&lan=zh&cuid=hqyj&ctp=1&aue=6&tok=%s", data, token);
    curl_free(data);
    
    //启动播放软件，通过管道写入音频数据
    FILE* fp = popen("aplay -q -", "w");
    if (fp == NULL)
    {
        perror("fopen() failed");
        
    }

    curl_easy_setopt(curl, CURLOPT_URL, "https://tsn.baidu.com/text2audio");
    //配置客户端，使用HTTP的POST方法发送请求消息
    curl_easy_setopt(curl, CURLOPT_POST, 1);
    //配置需要通过POST请求消息发送给服务器的数据
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postdata);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
    //发送HTTP请求消息，等待服务器的响应消息
    CURLcode error = curl_easy_perform(curl);
    if (error != CURLE_OK)
    {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(error));
        curl_easy_cleanup(curl);
        free(postdata);
        pclose(fp);
    }

    //释放HTTP客户端申请的资源
    curl_easy_cleanup(curl);
    free(postdata);

    //关闭管道
    pclose(fp);
}


//录音：arecord -t raw -r 16000 -f S16_LE -c 1 test.pcm
//播放：aplay -t raw -r 16000 -f S16_LE -c 1 test.pcm
int main()
{
    //指向音频数据的指针
    char* audio = NULL;

    char* robot_response = NULL;

    char* response1 = NULL;

    char* stt_response = NULL;

    char* apikey = "4ecb82bf1f754c69bad856cafab0b817";
    //音频数据大小
    size_t size;
    //读取PCM文件内容
    size = stt_load_file("test.pcm", &audio);
    if (size == 0)
    {
        return EXIT_FAILURE;
    }

    char* token = get_token("8djYFrD7sykxUipk3G5V1NYZ", "zZeacAk9G6Gd8yhGVj7kg3RK0Mu3AuX3");
    if (NULL == token)
    {
        free(audio);
        return EXIT_FAILURE;
    }
    //将语音数据发送给云服务器，等待响应报文
    char* response = stt_send_request(token, audio, size);
    
    free(audio);
    if(response == NULL)
    {
        return EXIT_FAILURE;
    }
    //处理响应报文，将文本信息打印到屏幕上
    stt_response = stt_process_response(response);

    char* request = robot_make_request(apikey, stt_response);
        
   //将请求报文发送给图灵机器人
    response1 = robot_send_request(request);
        
    free(request);
    
    robot_response = robot_process_response(response1);
  
    text2speech(token, robot_response);
    
    
    free(response1);
    free(stt_response);
    free(robot_response);  
    free(token);
    free(response);
    
    return 0;
}
